package main

import (
	"bytes"
	"fmt"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"os/user"
	"strings"
	"syscall"
	"time"
)

func main()  {
	//osEnvMain()
	//osProMain()
	//osFileMain()
	execMain()
	//signalMain()
	//userMain()
}


func osEnvMain() {
	// 设置环境变量值
	os.Setenv("NAME","Gopher")

	// 使用指定函数替换s中的 $ 变量
	osExpand := os.Expand("Hello $NAME!",func(s string) string { return "Gopher" })
	fmt.Println(osExpand)

	// 根据现有变量 替换s中 $ 变量，引用未定义环境变量会被替换为空字符串
	osExpandEnv := os.ExpandEnv("Hello ${NAME}!")
	fmt.Println(osExpandEnv)

	// 根据键值检索环境变量值，若变量不存在则值为空
	osGetenv := os.Getenv("NAME")
	fmt.Println(osGetenv)

	// 根据键值检索环境变量值，若变量不存在则值为空,
	// 如果环境中存在变量，则返回 值（可能为空），布尔值为真。
	// 否则返回值为空，布尔值为false。
	osLookupEnvString,osLookupEnvBool := os.LookupEnv("NAME")
	fmt.Println(osLookupEnvString,osLookupEnvBool)

	// 返回环境变量字符串切片的拷贝，格式为 "key=value"
	fmt.Println(os.Environ())

	// 取消设置单个环境变量
	os.Unsetenv("NAME")

	//// 删除所有的环境变量
	//os.Clearenv()

	// 返回内核报告的主机名。
	fmt.Println(os.Hostname())

	// 返回基础系统的内存页大小。
	fmt.Println(os.Getpagesize())

}

func osProMain()  {
	// 初始化一个 保管将被StartProcess函数用于一个新进程的属性
	attr := &os.ProcAttr{
		Files: []*os.File{os.Stdin, os.Stdout, os.Stderr}, //其他变量如果不清楚可以不设定
	}

	// 使用提供的属性、程序名、命令行参数开始一个新进程
	p, err := os.StartProcess("G:\\python\\python3.7\\python.exe", []string{"G:\\python\\python3.7\\python.exe ", " G:\\goData\\go源码\\test.py"}, attr)
	if err != nil {
		fmt.Println(err)
	}

	// 根据进程id查找一个运行中的进程
	p, err = os.FindProcess(p.Pid)
	if err != nil {
		fmt.Println(err)
	}

	// 返回调用的pid
	fmt.Println(os.Getpid())
	// 返回调用的父pid
	fmt.Println(os.Getppid())

	//// 在不等待的情况下调用
	//// 释放进程相关的任何资源，使其在其后无法使用
	//p.Release()

	//// 向进程发送信号
	//p.Signal(syscall.SIGKILL)

	// 等待进程退出，返回其状态 ProcessState
	osProcessState,_ := p.Wait()

	// 返回已退出进程及其子进程的用户CPU时间。
	fmt.Println(osProcessState.UserTime())

	// 返回已退出进程及其子进程的系统CPU时间。
	fmt.Println(osProcessState.SystemTime())

	// 程序是否已经退出
	fmt.Println(osProcessState.Exited())

	// 程序是否成功退出
	fmt.Println(osProcessState.Success())

	// 返回进程的系统相关退出信息。将其转换为相应的基础类型，例如syscall
	fmt.Println(osProcessState.Sys())

	// 返回与系统相关的资源使用信息 已退出的进程。
	// 将其转换为相应的基础 类型，例如*syscall。
	fmt.Println(osProcessState.SysUsage())

	// 立刻退出进程
	if err := p.Kill(); err != nil {
		fmt.Println(err)
	}


	// 返回调用者用户id
	fmt.Println(os.Getuid())
	// 返回调用者的有效用户id
	fmt.Println(os.Geteuid())
	// 返回调用者的数字组id
	fmt.Println(os.Getgid())
	// 返回调用者的数字有效组id
	fmt.Println(os.Getegid())
	// 返回调用者所属组的数字ID列表
	fmt.Println(os.Getgroups())

	// 当前程序以给定的状态代码退出。
	fmt.Println(os.Exit)

}

func osFileMain()  {
	// 返回执行文件的符号链接或它指向的路径
	fmt.Println(os.Executable())

	// 在目录dir中创建一个新的临时文件， 打开文件进行读写，并返回结果文件。
	fmt.Println(os.CreateTemp("","*-log"))

	// 在目录dir中创建一个新的临时目录 并返回新目录的路径名。
	fmt.Println(os.MkdirTemp("","*logs"))

	// 返回一个描述命名文件的FileInfo。
	osStat,_ := os.Stat("test.txt")

	// 返回描述命名文件的文件信息。 如果文件是符号链接，则返回的FileInfo
	//描述符号链接。Lstat没有试图跟踪链接。
	osLstat,_ := os.Lstat("test.txt")

	// 返回一个文件系统（一个fs.fs），用于以目录dir为根的文件树。
	fmt.Println(os.DirFS("."))

	// 读取命名目录，返回按文件名排序的所有目录项。
	osReadDir,_ := os.ReadDir(".")
	fmt.Println(osReadDir[0].Info())

	// 打开文件进行读取，
	osOpen,_ := os.Open(".")
	/*
		// 读取与文件关联的目录的内容，并返回 n 个FileInfo结构，
		// n < 0 则返回所有
		osOpenReaddir,_ := osOpen.Readdir(-1)
		fmt.Println(osOpenReaddir[0].Name())

		// 读取与文件关联的目录的内容，并按目录顺序返回目录中最多n个文件名
		// n < 0 则返回所有
		osOpenReaddirnames,_ := osOpen.Readdirnames(3)
		fmt.Println(osOpenReaddirnames)
	*/
	// 读取与文件关联的目录的内容，并按目录顺序返回 n 个DirEntry值。
	// n < 0 则返回所有
	osOpenReadDir,_ := osOpen.ReadDir(-1)
	fmt.Println(osOpenReadDir[0].Info())


	// 创建或截断文件
	osfileCreate,_ := os.Create("test.txt")

	// 打开文件进行读取，返回文件描述符
	osFileOpen,osFileOpenErr := os.Open("test.txt")
	if osFileOpenErr != nil {
		fmt.Println(osFileOpenErr)
	}

	// 返回打开文件的名称
	fmt.Println(osFileOpen.Name())

	// 从文件中最多读取len（b）个字节，并将它们存储在b中。 它返回读取的字节数
	fmt.Println(osFileOpen.Read([]byte("aabbcc")))

	// 从字节偏移量off开始读取文件中的len（b）字节。 返回读取的字节数
	fmt.Println(osFileOpen.ReadAt([]byte("aabbcc"),2))

	// 从 r 中读取数据并写入文件
	fmt.Println(osfileCreate.ReadFrom(strings.NewReader("aaabbbcccdddeeefff")))

	// 将len（b）字节从b写入文件。返回写入的字节数
	fmt.Println(osfileCreate.Write([]byte("ggghhh")))

	// 将len（b）字节从字节偏移量off开始写入文件。 返回写入的字节数和
	fmt.Println(osfileCreate.WriteAt([]byte("ggghhh"),3))

	// 设置文件下次读写的偏移量，
	// 0表示相对于文件原点，1表示相对于当前偏移量，2表示相对于结束
	fmt.Println(osfileCreate.Seek(3,0))	// 从文件的原点开始，偏移3

	// 向文件写入字符串
	fmt.Println(osfileCreate.WriteString("zzzzzzzzzzzzzzzzzz"))

	// 使用指定的名称和权限创建一个新的目录,文件存在则报错
	fmt.Println(os.Mkdir("test",0777))

	//// 将当前的目录改名
	//fmt.Println(os.Chdir("hellodir"))

	// 以读写方式打开文件，并且内容写入方式为添加，如果文件不存在以0755操作模式创建文件
	f, err := os.OpenFile("test.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
	if err != nil {
		fmt.Println(err)
	}
	// 关闭文件
	defer f.Close()

	//// 重新命名文件
	//fmt.Println(os.Rename("test.txt","text.txt"))

	// 返回用于临时文件的默认目录。
	fmt.Println(os.TempDir())

	// 回用于用户特定 缓存数据的默认根目录。
	// 用户应该在这个目录中创建自己的特定于应用程序的子目录 并使用它。
	fmt.Println(os.UserCacheDir())

	// 返回用于特定于用户的 配置数据。
	// 用户应该在这个目录中创建自己的特定于应用程序的 子目录并使用它。
	fmt.Println(os.UserConfigDir())

	// 返回当前用户的主目录
	fmt.Println(os.UserHomeDir())

	// 更改文件的权限
	fmt.Println(os.Chmod("test.txt",0600))
	fmt.Println(osfileCreate.Chmod(0777))

	// 设置特殊文件的读写截止日期。如管道
	fmt.Println(osfileCreate.SetDeadline(time.Date(2222,time.November,24,11,1,30,0,time.UTC)))
	// 设置读取调用的截止日期
	fmt.Println(osfileCreate.SetReadDeadline(time.Date(2222,time.November,24,11,1,30,0,time.UTC)))
	// 设置写入调用的截止日期
	fmt.Println(osfileCreate.SetWriteDeadline(time.Date(2222,time.November,24,11,1,30,0,time.UTC)))

	// 返回一个原始文件，实现系统调用和控制接口
	fmt.Println(osfileCreate.SyscallConn())

	// 读取文件并返回文件内容
	fmt.Println(os.ReadFile("test.txt"))

	// 将数据写入命名文件，必要时创建该文件。
	// 如果文件不存在，WriteFile将使用perm权限创建它（在umask之前）；
	// 否则，WriteFile会在写入前将其截断，而不会更改权限。
	fmt.Println(os.WriteFile("test.txt",[]byte("aaabbbccc"),0777))


	// 返回当前目录对应的根路径名。如果当前目录可以是 通过多个路径到达（由于符号链接），
	// 则 Getwd可以返回其中任何一个路径。
	fmt.Println(os.Getwd())

	// 创建目录，可以创建多层级目录
	fmt.Println(os.MkdirAll("test/test/test",0777))
	// 删除路径及其包含的所有子级。
	fmt.Println(os.RemoveAll("test/test/test"))

	// 报告fi1和fi2是否描述同一文件。
	fmt.Println(os.SameFile(osStat,osLstat))

}

func execMain()  {
	cmd := exec.Command("go", "doc", "exec")
	// 设置输入内容
	cmd.Stdin = strings.NewReader("")

	// 声明buffer
	var out bytes.Buffer

	// 设置输出内容填充地址
	cmd.Stdout = &out

	// 执行c包含的命令，并阻塞直到完成
	err := cmd.Run()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(out.String())

	// 启动指定的命令，但不等待它完成。
	fmt.Println(cmd.Start())
	// 等待命令退出，并等待复制到 stdin或从stdout或stderr复制完成。
	fmt.Println(cmd.Wait())

	// 运行命令并返回其标准输出。
	fmt.Println(cmd.Output())

	// 运行命令并返回其组合的标准 输出和标准错误。
	fmt.Println(cmd.CombinedOutput())

	// C 描述命令
	fmt.Println(cmd.String())

	// 返回一个管道，该管道将在命令启动时连接到命令的 标准输入。
	fmt.Println(cmd.StdinPipe())

	// 返回一个管道，该管道将在命令启动时连接到命令的 标准输出。
	fmt.Println(cmd.StdoutPipe())

	// 返回一个管道，该管道将在命令启动时连接到命令的 标准错误。
	fmt.Println(cmd.StderrPipe())

	// PATH环境变量命名的 目录中搜索名为文件的可执行文件
	fmt.Println(exec.LookPath("aaa"))
}

func signalMain()  {

	// 设置发送信号通知的通道。
	c := make(chan os.Signal, 1)
	// 将信号中继到c
	signal.Notify(c, syscall.SIGINT, syscall.SIGKILL)
	s := <-c
	fmt.Println("Got signal:", s)

	// 忽略信号
	signal.Ignore(syscall.SIGKILL)

	// 是否已经忽略了信号
	signal.Ignored(syscall.SIGKILL)

	// 信号重置
	signal.Reset(syscall.SIGKILL)

	// 信号停止
	signal.Stop(c)

}

func userMain()  {
	// 返回当前的用户
	osUserCurrent,_ := user.Current()
	fmt.Println(osUserCurrent.Name)
	fmt.Println(osUserCurrent.Gid)
	fmt.Println(osUserCurrent.HomeDir)
	fmt.Println(osUserCurrent.Uid)
	fmt.Println(osUserCurrent.Username)
	fmt.Println(osUserCurrent.GroupIds())

	// 按用户名查找用户
	osUserLookup,_ := user.Lookup("Administrator")
	fmt.Println(osUserLookup.HomeDir)

	// 通过userId查找用户
	osUserLookupId,_ := user.LookupId("S-1-5-21-1960634000-4232135062-2048785132-500")
	fmt.Println(osUserLookupId)

	// 按名称查找组
	osUserLookupGroup,_ := user.LookupGroup("")
	fmt.Println(osUserLookupGroup)

	// 按groupId查找组
	osUserLookupGroupId,_ := user.LookupGroupId("S-1-5-21-1960634000-4232135062-2048785132-513")
	fmt.Println(osUserLookupGroupId)

}
